#ifdef CONFIG_MOTHER_BOARD_LED
#include <led.h>
#include <sys/util.h>
#include <zephyr.h>
#include <gpio.h>
#include <board.h>
#include <pwm.h>
#include <device.h>
#include <init.h>

#define NRF52_LED_ON_LEVEL	0
#define NRF52_LED_OFF_LEVEL	1

#define NRF52_MIN_DELAY_UNIT	100
#define NRF52_MAX_BRIGHT		100

#define STACKSIZE 1024
#define PERIOD (USEC_PER_SEC / 50)

struct nrf52_ledparam{
	u8_t led;
	u8_t delay_on;
	u8_t delay_off;
	u8_t brightness;
	u8_t cnt;
	u8_t mode;
};

struct nrf52led_data {
	struct device *gpio;
	struct device *pwm;
	struct nrf52_ledparam param[LED_NUMBER];
};

static K_THREAD_STACK_DEFINE(stack, STACKSIZE);
static struct k_thread led_thread;
static struct nrf52led_data nrf52_led_data;

struct nrf52_ledparam* _get_led_param(struct device *dev, u32_t led)
{
	u8_t i;
	struct nrf52led_data *data = dev->driver_data;
	for(i=0; i<LED_NUMBER; i++){
		if(data->param[i].led == led){
			return &data->param[i];
		}
	}

	return NULL;
}


static int nrf52_led_blink(struct device *dev, u32_t led,
			    u32_t delay_on, u32_t delay_off)
{
    printk("%s[%d] %d-%d\n",__FUNCTION__,led,delay_on,delay_off);
	struct nrf52_ledparam *ledparam = _get_led_param(dev, led);
	if(ledparam == NULL){
		printk("Failed to get led param %d\n", led);
		return -EINVAL;
	}

	if(NRF52_MIN_DELAY_UNIT > delay_on || NRF52_MIN_DELAY_UNIT > delay_off){
		printk("Failed to set blink %d %d\n", delay_on, delay_off);
		return -EINVAL;
	}

	ledparam->delay_on = delay_on/NRF52_MIN_DELAY_UNIT;
	ledparam->delay_off = delay_off/NRF52_MIN_DELAY_UNIT;
	ledparam->mode = 1;
	ledparam->cnt = ledparam->delay_on;

	return 0;
}

static int nrf52_led_set_brightness(struct device *dev, u32_t led,
				     u8_t value)
{
    printk("%s[%d] %d\n",__FUNCTION__,led,value);
	struct nrf52led_data *data = dev->driver_data;
	struct nrf52_ledparam *ledparam = _get_led_param(dev, led);
	if(ledparam == NULL){
		printk("Failed to get led param %d\n", led);
		return -EINVAL;
	}

	if(value > NRF52_MAX_BRIGHT){
		printk("Failed to set led brightness %d\n", value);
		return -EINVAL;
	}

	ledparam->brightness = value;

	if(ledparam->delay_on == 1 && ledparam->delay_off == 0){
		if(value == NRF52_MAX_BRIGHT){
			gpio_pin_write(data->gpio, led, NRF52_LED_ON_LEVEL);
		}else if(value == 0){
			gpio_pin_write(data->gpio, led, NRF52_LED_OFF_LEVEL);
		}else{
			pwm_pin_set_usec(data->pwm, led, PERIOD, PERIOD*value/NRF52_MAX_BRIGHT);
		}
	}
	return 0;
}

static inline int nrf52_led_on(struct device *dev, u32_t led)
{
    printk("%s[%d] on\n",__FUNCTION__,led);
	struct nrf52_ledparam *ledparam = _get_led_param(dev, led);
	if(ledparam == NULL){
		printk("Failed to get led param %d\n", led);
		return -EINVAL;
	}

	ledparam->delay_on = 1;
	ledparam->delay_off = 0;
	nrf52_led_set_brightness(dev,led, ledparam->brightness);
	return 0;
}

static inline int nrf52_led_off(struct device *dev, u32_t led)
{
    printk("%s[%d] off\n",__FUNCTION__,led);
	struct nrf52led_data *data = dev->driver_data;
	struct nrf52_ledparam *ledparam = _get_led_param(dev, led);
	if(ledparam == NULL){
		printk("Failed to get led param %d\n", led);
		return -EINVAL;
	}

	ledparam->delay_on = 0;
	ledparam->delay_off = 1;
	gpio_pin_configure(data->gpio,led,(GPIO_DIR_OUT));
	gpio_pin_write(data->gpio, led, NRF52_LED_OFF_LEVEL);
	return 0;
}

static void led_thread_fun(void *p1, void *p2, void *p3)
{
	struct nrf52led_data *data = &nrf52_led_data;
	int i;

	ARG_UNUSED(p1);
	ARG_UNUSED(p2);
	ARG_UNUSED(p3);

	printk("nrf52 led driver thread\n");
	while (1) {
		for(i=0; i<LED_NUMBER; i++){
			struct nrf52_ledparam *ledparam = &(data->param[i]);
			if(ledparam->delay_on == 0 || ledparam->delay_off == 0){
				continue;
			}

			printk("ledparam->cnt %d ledparam->mode %d(%d,%d)\n", ledparam->cnt,
													ledparam->mode,
													ledparam->delay_on,
													ledparam->delay_off);

			if(ledparam->mode == 1){
				if(ledparam->delay_on == ledparam->cnt+1){
					printk("on\n");
					gpio_pin_configure(data->gpio,ledparam->led,(GPIO_DIR_OUT));
					gpio_pin_write(data->gpio, ledparam->led, NRF52_LED_ON_LEVEL);
					pwm_pin_set_usec(data->pwm, ledparam->led, PERIOD, PERIOD*ledparam->brightness/NRF52_MAX_BRIGHT);
				}
				else if(ledparam->cnt == 0){
					ledparam->mode = 0;
					ledparam->cnt = ledparam->delay_off;
				}

				ledparam->cnt--;
			}else{
				if(ledparam->delay_off == ledparam->cnt+1){
					printk("off\n");
					gpio_pin_configure(data->gpio,ledparam->led,(GPIO_DIR_OUT));
					gpio_pin_write(data->gpio, ledparam->led, NRF52_LED_OFF_LEVEL);
				}
				else if(ledparam->cnt == 0){
					ledparam->mode = 1;
					ledparam->cnt = ledparam->delay_on;
				}

				ledparam->cnt--;
			}

		}
		k_sleep(NRF52_MIN_DELAY_UNIT);
	}
}

static int nrf52_led_init(struct device *dev)
{
    printk("%s\n",__FUNCTION__);
	struct nrf52led_data *data = dev->driver_data;

	data->gpio = device_get_binding(LED_GPIO_PORT);
	data->pwm = device_get_binding(PWM_DRIVER);

	if (data->gpio == NULL) {
		printk("Failed to get GPIO device");
		return -EINVAL;
	}

	if (data->pwm == NULL) {
		printk("Failed to get PWM device");
		return -EINVAL;
	}

	data->param[0].led = LED_RED_PIN;
	data->param[0].brightness = NRF52_MAX_BRIGHT;
	data->param[1].led = LED_BLUE_PIN;
	data->param[1].brightness = NRF52_MAX_BRIGHT;
	data->param[2].led = LED_GREEN_PIN;
	data->param[2].brightness = NRF52_MAX_BRIGHT;

	nrf52_led_off(dev,LED_RED_PIN);
	nrf52_led_off(dev,LED_BLUE_PIN);
	nrf52_led_off(dev,LED_GREEN_PIN);

	k_thread_create(&led_thread, stack, STACKSIZE, led_thread_fun, NULL, NULL,
			NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
	return 0;
}

static const struct led_driver_api nrf52_led_api = {
	.blink = nrf52_led_blink,
	.set_brightness = nrf52_led_set_brightness,
	.on = nrf52_led_on,
	.off = nrf52_led_off,
};


DEVICE_AND_API_INIT(nrf52_led, "NRF_52",
		    &nrf52_led_init, &nrf52_led_data,
		    NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
		    &nrf52_led_api);
#endif